import { Sequelize } from "sequelize";
import { dataGet, empty } from "#Utils/helper.js";
import dbConfig from "#Config/database.js";
import AsyncLock from 'async-lock';
import { LogFacade } from "#Libs/logs/logger.js";

class DBContainer {
    constructor() {
        if (DBContainer.instance) {
            // 如果实例已存在，直接返回该实例
            return DBContainer.instance;
        }
        this.DBConnect = new Map();
        this.lock = new AsyncLock();
        this.closeLock = new AsyncLock();  // 用于保护 close 操作的锁
        this.healthCheckInterval = 60000;  // 默认每 60 秒执行一次健康检查
        this.closeConcurrencyLimit = 5; // 限制每次并发关闭的连接数
        DBContainer.instance = this;

        // // 启动定时健康检查
        // this.startHealthCheck();
    }

    async getDB(name = '') {
        if (empty(name)) {
            name = dataGet(dbConfig, "connection", "koa");
        }

        // 在获取数据库连接前，尝试获得锁
        await this.lock.acquire(name, async () => {
            // 获取连接，如果不存在则创建
            if (!this.DBConnect.has(name)) {
                await this.setDB(name);
            }
        });
        return this.DBConnect.get(name).DB;
    }

    async setDB(name) {
        const dbConfs = dataGet(dbConfig, "connections", {})

        const dbConf = dataGet(dbConfs, name, {});

        if (empty(dbConf)) {
            throw new Error(`Database configuration for ${name} not found.`);
        }
        // 如果数据库连接已存在，直接返回
        if (this.DBConnect.has(name)) {
            try {
                await this.DBConnect.get(name).DB.authenticate();
            } catch (error) {
                // 验证失败
                console.error(`Connection already exists, but authentication failed:`, error);
                // 关闭连接
                await this.close(name);
                throw error;
            }
            return;
        }


        this.DBConnect.set(name, await this.connect(dbConf));
    }

    async connectMysql(dbConf) {
        const options = {
            host: dbConf.host,
            dialect: dbConf.dialect,
            pool: dbConf.pool || {
                max: 5,
                min: 0,
                acquire: 30000,
                idle: 10000
            },
            define: {
                underscored: true,  // 自动将 `camelCase` 字段转换为 `snake_case`
            },
            logging: (sql, model) => {

                if (sql.trim() === 'SELECT 1+1 AS result') {
                    return false
                }

                if (dbConf.logging) {
                    LogFacade.channel('sql').info(`SQL: ${sql}`);
                    return true
                }
                return false
            }
        }
        // if (dbConf.logging) {
        //     const log = this.buildLog()
        //     options.logging = log
        // }


        const DB = new Sequelize(dbConf.database, dbConf.username, dbConf.password, options)
        try {
            await DB.authenticate()
            console.log(`Database connection for ${dbConf.database} established successfully.`)
        } catch (error) {
            console.error(`Unable to connect to the database:`, error)
            // 连接失败 抛出错误
            throw error;
        }

        return { DB };
    }

    async connectSqlite(dbConf) {
        const options = {
            storage: dbConf.storage,
            dialect: dbConf.dialect,
            pool: dbConf.pool || {
                max: 5,
                min: 0,
                acquire: 30000,
                idle: 10000
            },
            define: {
                underscored: true,  // 自动将 `camelCase` 字段转换为 `snake_case`
            }
        }
        const DB = new Sequelize(options)
        try {
            await DB.authenticate()
            console.log(`Database connection for ${dbConf.name} established successfully.`)
        } catch (error) {
            console.error(`Unable to connect to the database:`, error)
            // 连接失败 抛出错误
            throw error;
        }

        return { DB };
    }

    async connect(dbConf) {
        switch (dbConf.dialect) {
            case 'mysql':
                return await this.connectMysql(dbConf);
            case 'sqlite':
                return await this.connectSqlite(dbConf);
            default:
                throw new Error(`Unsupported dialect: ${dbConf.dialect}`);
        }
       
    }

    async close(name) {
        // 使用独立的锁来确保同一时刻只有一个关闭操作
        await this.closeLock.acquire(name, async () => {
            let connect = this.DBConnect.get(name);
            if (!connect) {
                throw new Error(`Database connection for ${name} not found.`);
            }

            // Sequelize 自动管理连接池，调用 `DB.close()` 会关闭连接池中的连接
            try {
                await connect.DB.close();
                console.log(`Database connection pool for ${name} closed successfully.`);
            } catch (error) {
                console.error(`Error closing connection pool for ${name}:`, error);
            }

            this.DBConnect.delete(name)
            console.log(`Database connection for ${name} closed successfully.`);
        })
    }

    async closeAll() {
        // 限制并发关闭操作的数量
        const entries = Array.from(this.DBConnect.entries());
        const chunkedEntries = this.chunk(entries, this.closeConcurrencyLimit);

        for (const chunk of chunkedEntries) {
            const closePromises = chunk.map(([name]) => this.close(name));
            await Promise.all(closePromises);
        }
    }

    chunk(array, size) {
        const result = [];
        for (let i = 0; i < array.length; i += size) {
            result.push(array.slice(i, i + size));
        }
        return result;
    }

    // 启动健康检查
    startHealthCheck() {
        setInterval(async () => {
            for (const [name, connect] of this.DBConnect) {
                try {
                    // 验证连接是否健康
                    await connect.DB.authenticate();
                    console.log(`Health check passed for ${name}`);
                } catch (error) {
                    console.error(`Health check failed for ${name}:`, error);
                    // 如果健康检查失败，重新建立连接
                    await this.close(name);
                    await this.setDB(name);
                }
            }
        }, this.healthCheckInterval);  // 每隔 60 秒检查一次
    }

    buildLog() {

    }
}

export default new DBContainer();
